using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Diagnostics;
using System.Configuration;
using System.Net;
using System.Net.Sockets;
using Microsoft.BizTalk.ExplorerOM;

namespace Microsoft.Samples.BizTalk.Adapter.Tcp.Receive
{
    /// <summary>
    ///     This class represents a duplex listener.
    /// </summary>
    /// <author>
    ///     Paul Brown
    /// </author>
    public class DuplexListener
    {
        #region Private Constants

        // The delay period between each timer tick when checking to see when all data has been read from the socket before
        // asynchronously blocking again.
        private const int ReceiveTimerDelay = 50;

        #endregion

        #region Private Instance Members

        private ReceiveAdapter  _receiveAdapter;    // The receive adapter
        private ReceiveLocation _receiveLocation;   // The receive location that is handling this duplex listener
        private Socket          _initiatorSocket;   // The socket associated with the original initiator
        private Socket          _duplexSocket;      // The socket associated with the duplex listener
        private Thread          _duplexListener;    // The duplex listener thread
        private bool            _terminateCalled;   // The adapter is being terminated by BizTalk
        private string          _duplexUrl;         // The internal dynamic address that the receive location will listen when it supports a duplex connection
        private string          _bizTalkPartyName;  // The name of the BizTalk Party that is used to contain the dynamic duplex address
        private AutoResetEvent  _waitEvent;         // Used to halt start until duplex URL has been set
        private byte[]          _receiveBuffer;     // Asynchronous data receive buffer
        private OrderedBatchWorkerThread _orderedWorkerThread; // The worker thread used for ordered batches

        #endregion

        #region Constructor

        /// <summary>
        ///     This constructor initializes the class.
        /// </summary>
        public DuplexListener(ReceiveAdapter adapter, ReceiveLocation receiveLocation, Socket initiatorSocket)
        {
            // Validate arguments
            if (adapter == null) throw new ArgumentNullException("adapter");
            if (receiveLocation == null) throw new ArgumentNullException("receiveLocation");
            if (initiatorSocket == null) throw new ArgumentNullException("initiatorSocket");

            // Initialize members
            this._terminateCalled = false;
            this._receiveAdapter = adapter;
            this._receiveLocation = receiveLocation;
            this._initiatorSocket = initiatorSocket;
            this._duplexSocket = null;
            this._duplexListener = null;
            this._duplexUrl = null;
            this._bizTalkPartyName = string.Empty;
            this._waitEvent = new AutoResetEvent(false);
            this._orderedWorkerThread = null;
        }

        #endregion

        #region Public Instance Events

        /// <summary>
        ///     This event is fired just before the listener thread terminates.
        /// </summary>
        public event EventHandler ThreadTerminated;

        #endregion

        #region Public Instance Properties

        /// <summary>
        ///     This property returns a reference to the receive adapter.
        /// </summary>
        public ReceiveAdapter Adapter
        {
            get { return (this._receiveAdapter); }
        }

        /// <summary>
        ///     This property returns a reference to the receive location.
        /// </summary>
        public ReceiveLocation ReceiveLocation
        {
            get { return (this._receiveLocation); }
        }

        /// <summary>
        ///     This property returns the address that the duplex listener is using internally.
        /// </summary>
        public string DuplexUrl
        {
            get { return (this._duplexUrl); }
        }

        /// <summary>
        ///     This property returns the BizTalk Party name that the duplex listener has added its internal socket address to.
        /// </summary>
        public string BizTalkParty
        {
            get { return (this._bizTalkPartyName); }
        }

        #endregion

        #region Public Instance Methods

        /// <summary>
        ///     This method starts the duplex listener.
        /// </summary>
        public void Start()
        {
            if (this.Adapter.TraceSwitch.TraceVerbose)
                TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveStartingDuplexListener, this.ReceiveLocation.Uri);

            // Set up an ordered worker thread if this receive location is to be ordered
            if (this.ReceiveLocation.IsOrdered)
            {
                if (this.Adapter.TraceSwitch.TraceVerbose)
                    TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveStartingOrderedWorkerThread, this.ReceiveLocation.Uri);

                // Start thread
                this._orderedWorkerThread = new OrderedBatchWorkerThread(this.Adapter);
                this._orderedWorkerThread.Start();
            }

            // Create listener depending on whether it should use a dedicated thread
            if (this.Adapter.UseBlockingSockets)
            {
                this.CreateSyncDuplexListener();
            }
            else
            {
                this.CreateAsyncDuplexListener();
            }

            // Update BizTalk Party with IP address details
            this._bizTalkPartyName = this.UpdateBizTalkParty(this._initiatorSocket.RemoteEndPoint as IPEndPoint, this.DuplexUrl);

            if (this.Adapter.TraceSwitch.TraceVerbose)
                TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveStartedDuplexListener, this.ReceiveLocation.Uri);
        }

        /// <summary>
        ///     This method stops the duplex listener.  It will block until it has stopped.
        /// </summary>
        public void Stop()
        {
            if (this.Adapter.TraceSwitch.TraceVerbose)
                TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveStoppingDuplexListener, this.ReceiveLocation.Uri);

            // Set flag (duplex listener polls so it will shut down itself by setting flag)
            this._terminateCalled = true;

            // Close the duplex socket
            if (this._duplexSocket != null)
            {
                this._duplexSocket.Close(this.ReceiveLocation.DuplexCloseTimeout);
            }

            // Close the initiator socket
            if (this._initiatorSocket != null)
            {
                this._initiatorSocket.Close(this.ReceiveLocation.ClientCloseTimeout);
            }

            // Stop ordered worker thread if we are ordered
            if (this.ReceiveLocation.IsOrdered)
            {
                if (this.Adapter.TraceSwitch.TraceVerbose)
                    TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveStoppingOrderedWorkerThread, this.ReceiveLocation.Uri);

                // Stop thread
                this._orderedWorkerThread.Stop();
            }

            // Remove duplex address from BizTalk Party
            this.ClearBizTalkParty(this.BizTalkParty);

            if (this.Adapter.TraceSwitch.TraceVerbose)
                TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveStoppedDuplexListener, this.ReceiveLocation.Uri);
        }

        #endregion

        #region Protected Instance Methods

        /// <summary>
        ///     This method invokes the ThreadTerminated event.
        /// </summary>
        protected virtual void OnThreadTerminated(EventArgs e)
        {
            if (this.ThreadTerminated != null)
            {
                this.ThreadTerminated(this, e);
            }
        }

        #endregion

        #region Private Instance Methods

        /// <summary>
        ///     This method starts the internal listening thread for the receive location when in duplex mode.
        /// </summary>
        private void CreateSyncDuplexListener()
        {
            // Create IP endpoint to listen on
            IPEndPoint ipEndpoint = new IPEndPoint(IPAddress.Any, 0);

            // Create and start thread
            this._duplexListener = new Thread(new ParameterizedThreadStart(this.DuplexListen));
            this._duplexListener.Name = TraceHelper.ResourceManager.GetString(ResourceConstants.DuplexListenerThreadNamePrefix) + this._duplexListener.ManagedThreadId.ToString();
            this._duplexListener.IsBackground = true;
            this._duplexListener.Start(ipEndpoint);

            // Wait for thread to signal that it has created the address
            this._waitEvent.WaitOne();

            // Get local endpoint
            IPEndPoint localEndpoint = (IPEndPoint)this._duplexSocket.LocalEndPoint;

            if (this.Adapter.TraceSwitch.TraceVerbose)
                TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveCreatedSyncDuplexListener, localEndpoint.Port);
        }

        /// <summary>
        ///     This method starts an asynchronous internal listening thread that uses the .NET thread pool for the receive location.
        /// </summary>
        private void CreateAsyncDuplexListener()
        {
            // Create IP endpoint to listen on
            IPEndPoint ipEndpoint = new IPEndPoint(IPAddress.Any, 0);

            // Create socket, bind, listen
            this._duplexSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);
            this._duplexSocket.Bind(ipEndpoint);
            this._duplexSocket.Listen(this.ReceiveLocation.MaxPendingConnections);

            // Set address
            IPEndPoint localEndpoint = (IPEndPoint)this._duplexSocket.LocalEndPoint;
            this._duplexUrl = this.Adapter.TransportType + "://" + Dns.GetHostName() + ":" + localEndpoint.Port.ToString();

            if (this.Adapter.TraceSwitch.TraceVerbose)
                TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveCreatedAsyncDuplexListener, localEndpoint.Port);

            // Begin accepting connections from the duplex socket
            IAsyncResult result = this._duplexSocket.BeginAccept(new AsyncCallback(this.AcceptCallback), localEndpoint);

            // Begin peeking from the initiator socket
            this._receiveBuffer = new byte[this.ReceiveLocation.SocketReceiveBufferSize];
            this._initiatorSocket.BeginReceive(this._receiveBuffer, 0, this.ReceiveLocation.SocketReceiveBufferSize, SocketFlags.Peek, new AsyncCallback(this.ReceiveCallback), null);

            if (this.Adapter.TraceSwitch.TraceVerbose)
                TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveStartingDuplexAsyncListener);
        }

        /// <summary>
        ///     This method is a ThreadStart delegate that starts up a duplex specific listener for
        ///     messages going back out an initiated connection.
        /// </summary>
        private void DuplexListen(object data)
        {
            if (this.Adapter.TraceSwitch.TraceVerbose)
                TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveStartingDuplexListenerSyncThread);

            bool duplexSocketClosed = false;
            bool initiatorSocketClosed = false;
            try
            {
                // Get IPEndPoint from data
                IPEndPoint ipEndpoint = (IPEndPoint)data;

                // Create socket, bind and then listen
                this._duplexSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);
                this._duplexSocket.Bind(ipEndpoint);
                this._duplexSocket.Listen(this.ReceiveLocation.MaxPendingConnections);

                // Set address
                IPEndPoint localEndpoint = (IPEndPoint)this._duplexSocket.LocalEndPoint;
                this._duplexUrl = this.Adapter.TransportType + "://" + Dns.GetHostName() + ":" + localEndpoint.Port.ToString();

                // Release thread that started us
                this._waitEvent.Set();

                // Block in thread until it is time to stop
                while ((!this._terminateCalled) && (!initiatorSocketClosed))
                {
                    // Client socket variable
                    Socket clientSocket = null;

                    //if (this.Adapter.TraceSwitch.TraceVerbose)
                    //    TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveBlockingOnPoll, ipEndpoint.Port, this.ReceiveLocation.PollTimeout);

                    // Poll the socket for new connections
                    if (this._duplexSocket.Poll(this.ReceiveLocation.PollTimeout, SelectMode.SelectRead))
                    {
                        // If we have a new connection, accept it
                        clientSocket = this._duplexSocket.Accept();

                        if (this.Adapter.TraceSwitch.TraceVerbose)
                            TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveAcceptedNewSocket, (this._duplexSocket.LocalEndPoint as IPEndPoint).Port, (clientSocket.RemoteEndPoint as IPEndPoint).Address, (clientSocket.RemoteEndPoint as IPEndPoint).Port);

                        // Set socket options: receive timeout, send timeout, receive buffer size, send buffer size
                        clientSocket.ReceiveBufferSize = this.ReceiveLocation.SocketReceiveBufferSize;
                        clientSocket.ReceiveTimeout = this.ReceiveLocation.DuplexReceiveTimeout;
                        clientSocket.SendBufferSize = this.ReceiveLocation.SocketSendBufferSize;
                        clientSocket.SendTimeout = this.ReceiveLocation.SocketSendTimeout;

                        // Pass work off to dispatcher
                        this.Adapter.Dispatcher.AddWorkItem(new DuplexWorkItem(this.ReceiveLocation, clientSocket, this._initiatorSocket) as IWorkItem);
                    }

                    // Check to see if data is available on the initiated socket for receiving, if so, submit batch work item
                    if (this._initiatorSocket.Available > 0)
                    {
                        if (this.Adapter.TraceSwitch.TraceVerbose)
                            TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveDataAvailableOnInititatorSocket, this.ReceiveLocation.Uri, this._initiatorSocket.Available);

                        // Pass socket to dispatcher only if this is not an ordered receive location, otherwise deal with
                        // socket by passing work to an OrderedBatchWorkerThread directly and waiting.
                        if (this.ReceiveLocation.IsOrdered)
                        {
                            if (this.Adapter.TraceSwitch.TraceVerbose)
                                TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveSubmittingOrderedWorkItem, this.ReceiveLocation.Uri);

                            // Pass socket to ordered worker thread directly (this will block until it is finished)
                            this._orderedWorkerThread.DoWork(new OrderedBatchWorkItem(this.ReceiveLocation, this._initiatorSocket) as IWorkItem);
                        }
                        else
                        {
                            // Pass socket to dispatcher
                            this.Adapter.Dispatcher.AddWorkItem(new BatchWorkItem(this.ReceiveLocation, this._initiatorSocket) as IWorkItem);
                        }
                    }

                    // Has the initiator socket been disconnected?  Detecting a dead client session is a right pain!
                    // This works but is a bit convoluted.
                    if ((this._initiatorSocket.Poll(this.ReceiveLocation.PollTimeout, SelectMode.SelectRead)) &&
                        (this._initiatorSocket.Available == 0))
                    {
                        if (this.Adapter.TraceSwitch.TraceVerbose)
                            TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveInitiatorSocketClosedRemotely);

                        // Stop thread and close duplex socket
                        initiatorSocketClosed = true;
                    }
                }

                if (this.Adapter.TraceSwitch.TraceVerbose)
                    TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveTerminateCalled, Thread.CurrentThread.Name);
            }
            catch (ObjectDisposedException e)
            {
                if (this.Adapter.TraceSwitch.TraceVerbose)
                    TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveSocketDisposedError, e.Message);

                // Socket has been closed by Stop(), just end
                duplexSocketClosed = true;
            }
            catch (SocketException e)
            {
                // Handle socket error
                SocketErrorHandler.HandleError(this.Adapter, this.ReceiveLocation, e, false);
            }
            catch (Exception e)
            {
                // We have aborted somehow but not by Stop().  The recipient of the ThreadTerminated event should implement remedial action.
                TraceHelper.WriteEntry(TraceLevel.Error, ResourceConstants.ReceiveGeneralError, e.Message);
            }
            finally
            {
                // Close the socket
                if (!duplexSocketClosed)
                {
                    this._duplexSocket.Close(this.ReceiveLocation.DuplexCloseTimeout);
                }

                // Fire terminate event
                this.OnThreadTerminated(new EventArgs());
            }
        }

        /// <summary>
        ///     This method represents the delegate for the asynchronous callback of the BeginAccept() call on the socket.
        /// </summary>
        private void AcceptCallback(IAsyncResult asyncResult)
        {
            if (this.Adapter.TraceSwitch.TraceVerbose)
                TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveSocketAcceptCallbackInvoked);

            // Get object state
            IPEndPoint endpoint = asyncResult.AsyncState as IPEndPoint;

            try
            {
                // Accept new socket
                Socket clientSocket = this._duplexSocket.EndAccept(asyncResult);

                if (this.Adapter.TraceSwitch.TraceVerbose)
                    TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveAcceptedNewSocket, endpoint.Port, (clientSocket.RemoteEndPoint as IPEndPoint).Address, (clientSocket.RemoteEndPoint as IPEndPoint).Port);

                // Set socket options: receive timeout, send timeout, receive buffer size, send buffer size
                clientSocket.ReceiveBufferSize = this.ReceiveLocation.SocketReceiveBufferSize;
                clientSocket.ReceiveTimeout = this.ReceiveLocation.DuplexReceiveTimeout;
                clientSocket.SendBufferSize = this.ReceiveLocation.SocketSendBufferSize;
                clientSocket.SendTimeout = this.ReceiveLocation.SocketSendTimeout;

                // Pass work off to dispatcher
                this.Adapter.Dispatcher.AddWorkItem(new DuplexWorkItem(this.ReceiveLocation, clientSocket, this._initiatorSocket) as IWorkItem);
            }
            catch (ObjectDisposedException e)
            {
                if (this.Adapter.TraceSwitch.TraceVerbose)
                    TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveSocketDisposedError, e.Message);

                // Socket closed - shut down
                this._terminateCalled = true;
            }
            catch (SocketException e)
            {
                // Handle socket error
                SocketErrorHandler.HandleError(this.Adapter, this.ReceiveLocation, e, false);
            }
            catch (Exception e)
            {
                // We have aborted somehow but not by Stop()
                TraceHelper.WriteEntry(TraceLevel.Error, ResourceConstants.ReceiveGeneralError, e.Message);
            }
            finally
            {
                // Accept new sockets if we are not terminating
                if ((!this._terminateCalled) && (this._duplexSocket != null))
                {
                    IAsyncResult result = this._duplexSocket.BeginAccept(this.AcceptCallback, endpoint);
                }
            }
        }

        /// <summary>
        ///     This method represents the delegate for the asynchronous callback of BeginReceive() call on the initiator socket.
        /// </summary>
        private void ReceiveCallback(IAsyncResult asyncResult)
        {
            if (this.Adapter.TraceSwitch.TraceVerbose)
                TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveSocketReceiveCallbackInvoked);

            try
            {
                // End receive - the socket flags specify that the data should be peeked, therefore we can set up a work item
                // to receive the data properly.
                this._initiatorSocket.EndReceive(asyncResult);

                if (this.Adapter.TraceSwitch.TraceVerbose)
                    TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveDataAvailableOnInititatorSocket, this.ReceiveLocation.Uri, this._initiatorSocket.Available);

                // Pass socket to dispatcher only if this is not an ordered receive location, otherwise deal with
                // socket by passing work to an OrderedBatchWorkerThread directly and waiting.
                if (this.ReceiveLocation.IsOrdered)
                {
                    if (this.Adapter.TraceSwitch.TraceVerbose)
                        TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveSubmittingOrderedWorkItem, this.ReceiveLocation.Uri);

                    // Pass socket to ordered worker thread directly (this will block until it is finished)
                    this._orderedWorkerThread.DoWork(new OrderedBatchWorkItem(this.ReceiveLocation, this._initiatorSocket) as IWorkItem);
                }
                else
                {
                    // Pass socket to dispatcher
                    this.Adapter.Dispatcher.AddWorkItem(new BatchWorkItem(this.ReceiveLocation, this._initiatorSocket) as IWorkItem);
                }

                // Start timer to determine when to BeginReceive() again.  We only want to do this when the socket has no
                // available data (that is, anything that already existed on the socket has been read).  Otherwise we will go into
                // a loop from BeginReceive() to the receive callback being invoked until the data has been read.
                AutoResetEvent waitObject = new AutoResetEvent(false);
                Timer timer = new Timer(new TimerCallback(this.ReceiveTimer_Timer), waitObject, DuplexListener.ReceiveTimerDelay, DuplexListener.ReceiveTimerDelay);
                waitObject.WaitOne();
                timer.Dispose();
            }
            catch (ObjectDisposedException e)
            {
                if (this.Adapter.TraceSwitch.TraceVerbose)
                    TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveSocketDisposedError, e.Message);

                // Socket closed - shut down
                this._terminateCalled = true;
            }
            catch (SocketException e)
            {
                // Handle socket error
                SocketErrorHandler.HandleError(this.Adapter, this.ReceiveLocation, e, false);
            }
            catch (Exception e)
            {
                // We have aborted somehow but not by Stop()
                TraceHelper.WriteEntry(TraceLevel.Error, ResourceConstants.ReceiveGeneralError, e.Message);
            }
            finally
            {
                // Keep peeking as long as we are not terminating
                if ((!this._terminateCalled) && (this._initiatorSocket != null))
                {
                    this._initiatorSocket.BeginReceive(this._receiveBuffer, 0, this._receiveBuffer.Length, SocketFlags.Peek, new AsyncCallback(this.ReceiveCallback), null);
                }
            }
        }

        /// <summary>
        ///     This event handler is called by the timer each time the period has elapsed.
        /// </summary>
        private void ReceiveTimer_Timer(object state)
        {
            // Check to see if socket still has data available
            if (this._initiatorSocket != null)
            {
                if (this._initiatorSocket.Available == 0)
                {
                    // Release wait object
                    AutoResetEvent waitObject = (AutoResetEvent)state;
                    waitObject.Set();
                }
            }
        }

        /// <summary>
        ///     This method updates the BizTalk Party with the duplex listener URL address.
        /// </summary>
        private string UpdateBizTalkParty(IPEndPoint client, string duplexAddress)
        {
            // Get catalog object
            BtsCatalogExplorer catalog = new BtsCatalogExplorer();
            catalog.ConnectionString = ReceiveAdapter.BizTalkManagementDatabaseConnectionString;

            Party bizTalkParty = null;

            try
            {
                // Get host name and all IP addresses for it
                IPHostEntry hostEntry = Dns.GetHostEntry(client.Address);
                string hostName = hostEntry.HostName.ToUpper();

                // Search for a party with a ComputerAddress alias that matches the either the host name or one of the IP addresses
                foreach (Party party in catalog.Parties)
                {
                    foreach (PartyAlias alias in party.Aliases)
                    {
                        // Check alias name
                        if (alias.Name == ReceiveLocation.BizTalkPartyAliasName)
                        {
                            // It is our own alias, check the qualifer
                            if (alias.Qualifier == ReceiveLocation.BizTalkPartyAliasQualifierDnsName)
                            {
                                // Does qualifier value equal the hostname?
                                if (alias.Value.ToUpper() == hostName)
                                {
                                    // Found party
                                    bizTalkParty = party;
                                    break;
                                }
                            }
                            else if (alias.Qualifier == ReceiveLocation.BizTalkPartyAliasQualifierIpAddress)
                            {
                                // Does qualifier value equal the IP address?
                                foreach (IPAddress address in hostEntry.AddressList)
                                {
                                    if (alias.Value == address.ToString())
                                    {
                                        // Found party
                                        bizTalkParty = party;
                                        break;
                                    }
                                }
                            }
                        }
                    }

                    // Did we find the party?
                    if (bizTalkParty != null)
                    {
                        break;
                    }
                }

                if (bizTalkParty == null)
                {
                    // Party not found, throw error
                    string message = string.Format(TraceHelper.ResourceManager.GetString(ResourceConstants.BizTalkPartyNotFoundByHost), hostName);
                    throw new BizTalkPartyNotFoundException(string.Empty, message);
                }

                // Add duplex address
                PartyAlias duplexAlias = null;
                foreach (PartyAlias alias in bizTalkParty.Aliases)
                {
                    if (alias.Name == ReceiveLocation.BizTalkPartyAliasName &&
                        alias.Qualifier == ReceiveLocation.BizTalkPartyAliasQualifierDuplexAddress)
                    {
                        duplexAlias = alias;
                        break;
                    }
                }

                if (duplexAlias == null)
                {
                    // Not found, create it
                    duplexAlias = bizTalkParty.AddNewAlias();
                    duplexAlias.Name = ReceiveLocation.BizTalkPartyAliasName;
                    duplexAlias.Qualifier = ReceiveLocation.BizTalkPartyAliasQualifierDuplexAddress;
                    duplexAlias.Value = duplexAddress;
                }
                else
                {
                    // Found, change its value
                    duplexAlias.Value = duplexAddress;
                }

                // Commit changes
                catalog.SaveChanges();

                if (this.Adapter.TraceSwitch.TraceVerbose)
                    TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.BizTalkUpdatedPartyWithDuplexAddress, bizTalkParty.Name, duplexAddress);

                // Return party name
                return (bizTalkParty.Name);
            }
            catch (Exception e)
            {
                // Discard any changes and throw error
                catalog.DiscardChanges();
                throw (e);
            }
        }

        /// <summary>
        ///     This method clears the duplex address from the BizTalk Party.  If the BizTalk Party could not be
        ///     found, it throws a BizTalkPartyNotFoundException exception.
        /// </summary>
        private void ClearBizTalkParty(string bizTalkPartyName)
        {
            // Get catalog object
            BtsCatalogExplorer catalog = new BtsCatalogExplorer();
            catalog.ConnectionString = ReceiveAdapter.BizTalkManagementDatabaseConnectionString;

            try
            {
                // Get party
                Party bizTalkParty = catalog.Parties[bizTalkPartyName];

                if (bizTalkParty == null)
                {
                    // Should exist, throw error!
                    throw new BizTalkPartyNotFoundException(bizTalkPartyName);
                }

                // Remove the duplex address alias
                PartyAlias duplexAlias = null;
                string duplexAddress = string.Empty;
                foreach (PartyAlias alias in bizTalkParty.Aliases)
                {
                    if (alias.Name == ReceiveLocation.BizTalkPartyAliasName &&
                        alias.Qualifier == ReceiveLocation.BizTalkPartyAliasQualifierDuplexAddress)
                    {
                        duplexAlias = alias;
                        duplexAddress = duplexAlias.Value;
                        break;
                    }
                }

                if (duplexAlias != null)
                {
                    // Found, remove it
                    bizTalkParty.RemoveAlias(duplexAlias);
                }

                // Commit changes
                catalog.SaveChanges();

                if (this.Adapter.TraceSwitch.TraceVerbose)
                    TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.BizTalkRemovedDuplexAddressFromParty, duplexAddress, bizTalkPartyName);
            }
            catch (Exception e)
            {
                // Discard any changes and throw error
                catalog.DiscardChanges();
                throw (e);
            }
        }

        #endregion
    }
}
